package xyz.labmem.base.sys.util

import cn.dev33.satoken.stp.StpUtil
import org.aspectj.lang.JoinPoint
import org.springframework.scheduling.concurrent.ScheduledExecutorFactoryBean
import xyz.labmem.base.core.annotion.BusinessLog
import xyz.labmem.base.core.error.AppException
import xyz.labmem.base.core.tool.*
import xyz.labmem.base.sys.module.log.entity.SysOPLog
import xyz.labmem.base.sys.module.log.entity.SysUserOnline
import xyz.labmem.base.sys.module.log.entity.SysVISLog
import xyz.labmem.base.sys.module.user.entity.SysUser
import java.util.*
import java.util.concurrent.ScheduledThreadPoolExecutor
import java.util.concurrent.TimeUnit
import javax.servlet.http.HttpServletRequest

/**
 * 日志管理器
 * @Author lty
 * @Date 2021/12/16 13:24
 */
class LogManager {

    companion object {
        /**
         * 异步操作记录日志的线程池
         */
        private val EXECUTOR = ScheduledThreadPoolExecutor(10, ScheduledExecutorFactoryBean())

        private val LOG_MANAGER = LogManager()

        @JvmStatic
        fun me(): LogManager {
            return LOG_MANAGER
        }

        @JvmStatic
        /**
         * 生成通用操作日志字段
         *
         * @author xuyuxiang
         * @date 2020/3/16 17:20
         */
        private fun fillCommonSysOpLog(
            sysOpLog: SysOPLog,
            account: String,
            businessLog: BusinessLog,
            joinPoint: JoinPoint
        ) {
            val className = joinPoint.target.javaClass.name
            val methodName = joinPoint.signature.name
            val param = joinPoint.getArgsToJsonStr()
            sysOpLog.apply {
                name = businessLog.title
                opType = businessLog.opType
                this.className = className
                this.methodName = methodName
                this.param = param
                this.account = account
            }
        }
    }

    /**
     * 异步执行日志的方法
     */
    private fun executeLog(task: TimerTask) {
        //日志记录操作延时
        EXECUTOR.schedule(task, 10, TimeUnit.MILLISECONDS)
    }

    /**
     * 登录日志
     */
    fun executeLoginLog(user: SysUser?, success: Boolean, failMessage: String?) : SysUserOnline{
        val sysVisLog = genBaseSysVisLog()
        val sysUserOnline = genSysUserOnline(user)
        val timerTask = object : TimerTask() {
            override fun run() {
                try {
                    sysVisLog.apply {
                        name = "登录"
                        this.success = if (success) 'Y' else 'N'
                        visType = "登录"
                        if (user != null) {
                            this.account = user.account
                        }
                        message = if (success)
                            "登录成功!"
                        else
                            "登录失败：$failMessage"
                    }.insert()
                } catch (e: Exception) {
                    throw AppException("创建登录日志异常!", e)
                }
            }
        }
        executeLog(timerTask)
        return sysUserOnline
    }

    /**
     * 登出日志
     */
    fun executeExitLog(account: String?) {
        val sysVisLog = genBaseSysVisLog()
        val timerTask = object : TimerTask() {
            override fun run() {
                try {
                    sysVisLog.apply {
                        name = "登出"
                        this.success = 'Y'
                        visType = "登出"
                        this.account = account
                        message = "登出成功!"
                    }.insert()
                } catch (e: Exception) {
                    throw AppException("创建登出日志异常!", e)
                }
            }
        }


        executeLog(timerTask)
    }

    /**
     * 操作日志
     */
    fun executeOperationLog(businessLog: BusinessLog, account: String, joinPoint: JoinPoint, result: String?) {
        val sysOpLog = genBaseSysOpLog()
        val timerTask = object : TimerTask() {
            override fun run() {
                try {
                    fillCommonSysOpLog(sysOpLog, account, businessLog, joinPoint)
                    sysOpLog.apply {
                        success = 'Y'
                        this.result = result
                        message = "成功"
                    }.insert()
                } catch (e: Exception) {
                    throw AppException("创建操作日志异常!", e)
                }
            }
        }
        executeLog(timerTask)
    }

    /**
     * 异常日志
     */
    fun executeExceptionLog(businessLog: BusinessLog, account: String, joinPoint: JoinPoint, exception: Exception) {
        val sysOpLog = genBaseSysOpLog()
        val timerTask = object : TimerTask() {
            override fun run() {
                try {
                    fillCommonSysOpLog(sysOpLog, account, businessLog, joinPoint)
                    sysOpLog.apply {
                        success = 'N'
                        message = Arrays.toString(exception.stackTrace)
                    }.insert()
                } catch (e: Exception) {
                    throw AppException("创建异常日志异常!", e)
                }
            }
        }
        executeLog(timerTask)
    }

    /**
     * 构建基础访问日志
     */
    private fun genBaseSysVisLog(): SysVISLog {
        val request: HttpServletRequest = getRequest()
        return SysVISLog().apply {
            ip = request.getIp()
            browser = request.getBrowser()
            os = request.getOS()
        }
    }
    /**
     * 构建在线用户
     */
    private fun genSysUserOnline(user: SysUser?): SysUserOnline {
        val request: HttpServletRequest = getRequest()
        return SysUserOnline().apply {
            id = StpUtil.getLoginId() as String?
            tokenId = StpUtil.getTokenInfo().getTokenValue()
            if (user != null) {
                userName = user.account
                loginName = user.username
            }
            ipaddr = request.getIp()
            loginLocation = AddressUtils().getRealAddressByIP(request.getIp())
            browser = request.getBrowser()
            os = request.getOS()
            loginTime = System.currentTimeMillis()
            tokenTimeout = StpUtil.getTokenTimeout()
            tokenActivityTimeout = StpUtil.getTokenActivityTimeout()

        }
    }

    /**
     * 构建基础操作日志
     */
    private fun genBaseSysOpLog(): SysOPLog {
        val request: HttpServletRequest = getRequest()
        return SysOPLog().apply {
            ip = request.getIp()
            browser = request.getBrowser()
            os = request.getOS()
            url = request.requestURI
            reqMethod = request.method
        }
    }

}